package com.gitee.zeric.codec.parser;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gitee.zeric.codec.check.CodeUniqueCheck;
import com.gitee.zeric.codec.constant.RuleCodeType;
import com.gitee.zeric.codec.exception.ParamErrorException;
import com.gitee.zeric.codec.generaters.CodeGenerater;
import com.gitee.zeric.codec.generaters.CodeGeneraterFactory;
import com.gitee.zeric.codec.generaters.RuleParam;
import com.gitee.zeric.codec.model.RealCodeModel;
import com.gitee.zeric.codec.model.RuleModel;
import com.gitee.zeric.codec.util.VUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 默认编码生成器
 * @author zj
 * @date 2021/12/14
 */
@Slf4j
@AllArgsConstructor
public class DefaultCodeRuleParser {

    /**
     * rule正则解析
     */
    private static final Pattern pattern = Pattern.compile("\\{([^}]*)\\}");

    private final CodeUniqueCheck codeUniqueCheck;

    private final RedisTemplate redisTemplate;

    private final StringRedisTemplate stringRedisTemplate;

    private final String servicePrefix;


    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }



    /**
     * 递归转化业务规则为系统与常量规则组合
     *
     * @param ruleCode
     * @return java.lang.String
     * @author zj
     * @date 2021/12/22 20:17
     */
    String parseRulePlaceHolder(String ruleCode) {
        //从redis读取
        RuleModel ruleGenerate = (RuleModel) redisTemplate.opsForValue().get(servicePrefix+CodeGenerater.REDIS_DB_CODE_RULE_PREFIX + ruleCode);
        if (ruleGenerate == null) {
            ruleGenerate = (RuleModel) redisTemplate.opsForValue().get(servicePrefix+CodeGenerater.REDIS_TMP_CODE_RULE_PREFIX + ruleCode);
        }
        if (ruleGenerate == null) {
            return "-1";
        }
        if (!StrUtil.equals(ruleGenerate.getRuleType(), "1")) {
            return "{" + ruleCode + "}";
        }
        String rulePlaceholder = ruleGenerate.getRulePlaceholder();
        //{ORDER_NUM}-{RANDOM}   BM-{DATE}-{RANDOM}-{RANDOM}
        String replace = rulePlaceholder;
        Matcher matcher = pattern.matcher(rulePlaceholder);
        while (matcher.find()) {
            String symbol = matcher.group();
            if (StrUtil.equals(symbol, ruleCode)) {
                throw new ParamErrorException("规则标识无法自解析！");
            }
            String regularSymbol = symbol.replace("{", "\\{").replace("}", "\\}");
            replace = replace.replaceFirst(regularSymbol, parseRulePlaceHolder(symbol.replace("{", "").replace("}", "")));
        }
        return replace;
    }

    /**
     * 缓存规则
     *
     * @author zj
     * @date 2022/4/19 13:38
     */
    public void cacheRule(List<RuleModel> ruleModels) {
        ruleModels.forEach(r -> redisTemplate.opsForValue().set(servicePrefix+CodeGenerater.REDIS_DB_CODE_RULE_PREFIX + r.getRuleCode(), r));
        ruleModels.stream().filter(r->StrUtil.equals(r.getRuleType(), RuleCodeType.BUSINESS_VARIBLE)).forEach(r -> {
            r.setRulePlaceholder(parseRulePlaceHolder(r.getRuleCode()));
            redisTemplate.opsForValue().set(servicePrefix+CodeGenerater.REDIS_DB_CODE_RULE_PREFIX + r.getRuleCode(), r);
        });

    }

    /**
     * 缓存临时规则
     *
     * @author zj
     * @date 2022/4/19 13:38
     */
    public void cacheTmpRule(ArrayList<RuleModel> ruleModels) {
        ruleModels.forEach(r -> redisTemplate.opsForValue().set(servicePrefix+CodeGenerater.REDIS_TMP_CODE_RULE_PREFIX + r.getRuleCode(), r));
        ruleModels.stream().filter(r->StrUtil.equals(r.getRuleType(), RuleCodeType.BUSINESS_VARIBLE)).forEach(r -> {
            r.setRulePlaceholder(parseRulePlaceHolder(r.getRuleCode()));
            redisTemplate.opsForValue().set(servicePrefix+CodeGenerater.REDIS_TMP_CODE_RULE_PREFIX + r.getRuleCode(), r);
        });
    }

    /**
     * 清除规则
     *
     * @author zj
     * @date 2022/4/19 13:38
     */
    public void cacheEvitRule(String ruleCode) {
        redisTemplate.delete(servicePrefix+CodeGenerater.REDIS_DB_INCRMENT_CODE_RULE_PREFIX + ruleCode);
        redisTemplate.delete(servicePrefix+CodeGenerater.REDIS_DB_CODE_RULE_PREFIX + ruleCode);
    }

    /**
     * 解析{}生成具体编码
     *
     * @param oldRuleCode 旧规则
     * @param ruleCode    新规则
     * @param ruleParam   校验参数
     * @return java.lang.String
     * @author zj
     * @date 2022/4/19 13:39
     */
    private String parseSystemRule(String oldRuleCode, String ruleCode, JSONObject ruleParam) {
        RuleModel ruleGenerate = (RuleModel) redisTemplate.opsForValue().get(servicePrefix+CodeGenerater.REDIS_DB_CODE_RULE_PREFIX + ruleCode);
        VUtils.isTure(ruleGenerate == null).throwMessage(String.format("对应编码规则找不到！rule=%s", ruleCode));
        //常量编码规则
        if (StrUtil.equals(ruleGenerate.getRuleType(), RuleCodeType.CONSTANT_VARIBLE)) {
            return ruleGenerate.getRuleValue();
        }
        CodeGenerater codeGenerater;
        try {
            codeGenerater = CodeGeneraterFactory.getGenerater(ruleGenerate.getImplementClass(), stringRedisTemplate);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            log.error("解析类出错！{}", ruleCode, e);
            throw new ParamErrorException(String.format("对应编码规则无法解析或找不到解析实现类！rule=%s", ruleCode));
        }
        VUtils.isTure(codeGenerater == null).throwMessage(String.format("对应编码规则无法解析或找不到解析实现类！rule=%s", ruleCode));
        return codeGenerater.generateCode(StrUtil.equals("com.gitee.zeric.codec.generaters.WaterGenerater", ruleGenerate.getImplementClass()) ? oldRuleCode : ruleCode, ruleParam);
    }

    /**
     * 解析ruleCode规则并生成编码
     *
     * @param ruleCode
     * @param ruleParamClass
     * @return java.lang.String
     * @author zj
     * @date 2021/12/16 15:31
     */
    public <T extends RuleParam> RealCodeModel parse(String oldCode, String ruleCode, Class<T> ruleParamClass) {
        //从redis读取
        RuleModel ruleGenerate = (RuleModel) redisTemplate.opsForValue().get(servicePrefix+CodeGenerater.REDIS_DB_CODE_RULE_PREFIX + ruleCode);
        if (ruleGenerate == null) {
            ruleGenerate = (RuleModel) redisTemplate.opsForValue().get(servicePrefix+CodeGenerater.REDIS_TMP_CODE_RULE_PREFIX + ruleCode);
        }
        Assert.notNull(ruleGenerate,String.format("对应编码规则找不到！rule=%s", ruleCode));
        //常量编码规则
        if (StrUtil.equals(ruleGenerate.getRuleType(), RuleCodeType.CONSTANT_VARIBLE)) {
            return new RealCodeModel(ruleGenerate.getRuleValue(),RuleCodeType.CONSTANT_VARIBLE);
        }

        if (StrUtil.equals(ruleGenerate.getRuleType(), RuleCodeType.SYSTEM_VARIBLE)) {
            return new RealCodeModel(parseSystemRule(oldCode, ruleCode, JSONObject.parseObject(ruleGenerate.getRuleParam())),RuleCodeType.SYSTEM_VARIBLE);
        }
        //业务类型编码规则处理
        String rulePlaceholder = ruleGenerate.getRulePlaceholder();
        VUtils.isTure(StrUtil.isBlank(rulePlaceholder)).throwMessage("缺少配置规则，ruleCode=" + oldCode);
        String replace = rulePlaceholder;
        //匹配解析
        Matcher matcher = pattern.matcher(rulePlaceholder);
        List<T> ruleParams = JSONArray.parseArray(ruleGenerate.getRuleParam(), ruleParamClass);
        int count = 0;
        while (matcher.find()) {
            VUtils.isTure(count + 1 > ruleParams.size()).throwMessage("配置参数与规则个数不匹配！，ruleParams=" + ruleParams.size());
            String symbol = matcher.group();
            String regularSymbol = symbol.replace("{", "\\{").replace("}", "\\}");
            replace = replace.replaceFirst(regularSymbol, parseSystemRule(oldCode, symbol.replace("{", "").replace("}", ""), (JSONObject) JSONObject.toJSON(ruleParams.get(count))));
            count++;
        }
        return new RealCodeModel(replace,RuleCodeType.BUSINESS_VARIBLE);
    }

    /**
     * 生成编码并保存至bitmap（自带去重,使用redis bitmap,去重效率 99%）
     *
     * @param oldCode
     * @param ruleCode
     * @param ruleParamClass
     * @param limit 当出现多少次重复时不再生成
     * @return java.lang.String
     * @author zj
     * @date 2022/4/19 13:42
     */
    <T extends RuleParam> String uniqParse(String oldCode, String ruleCode, Class<T> ruleParamClass,int limit) {
        String parse = "";
        for(int i=0;i<limit;i++){
            RealCodeModel realCodeModel = parse(oldCode, ruleCode, ruleParamClass);
            parse = realCodeModel.getRealCode();
            if(StrUtil.equals(realCodeModel.getRuleType(),RuleCodeType.CONSTANT_VARIBLE)||StrUtil.equals(realCodeModel.getRuleType(),RuleCodeType.SYSTEM_VARIBLE)){
                return parse;
            }
            //是否不重复且插入成功
            if(codeUniqueCheck.checkAndInsert(ruleCode, parse)){
                return parse;
            }
        }
        return parse;
    }


    /**
     * 批量生成编码并保存至bitmap （自带去重,使用redis bitmap,去重效率 99%）
     *
     * @param ruleCode
     * @param size
     * @param copyLimit 当出现多少次重复时不再生成
     * @return java.util.Set<java.lang.String>
     * @author zj
     * @date 2022/4/19 13:42
     */
    public List<String> batchUniqParse(String ruleCode, int size,int copyLimit) {
        List<String> codes = new ArrayList<>(size);
        while (codes.size() < size) {
            codes.add(uniqParse(ruleCode, ruleCode, RuleParam.class,copyLimit));
        }
        return codes;
    }

}
